package pinboard.dataLayer.net;

import android.util.Log;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;

import java.io.IOException;

import pinboard.dataLayer.CommHelper;
import pinboard.dataLayer.potocol.instructFormat.response.KeepAliveResponse;
import pinboard.dataLayer.potocol.instructFormat.response.LoginResponse;
import pinboard.dataLayer.potocol.op.FixedSizeLoader;
import pinboard.dataLayer.potocol.op.InstructClientDecoder;
import pinboard.dataLayer.potocol.op.InstructClientEncoder;

public class InstructClientPipe extends BaseClientPipe {

    public InstructClientPipe(NetAddressInfo address, ClientPipeListener listener) {
        super(address, listener);
    }

    public InstructClientPipe(NetAddressInfo address) {
        super(address);
    }

    protected boolean passKeepalive = true;//是否下送心跳消息
    protected long keepAliveInterval = 20 * 1000;
    protected LoginResponse LoginResponseData = null;//全部在锁内操作，无需volatile
    protected boolean loginReturned = false;//全部在锁内操作，无需volatile
    protected volatile String accessToken = null;//有keepAlive线程在操作，需要volatile
    protected FixedSizeLoader msgLoader = new FixedSizeLoader();
    protected Runner keepAliveRunner = null;

    @Override
    protected void invokeConnectSuccess(ActionCallback<PipeContext> callback, PipeContext context) {
        NetAddressInfo address = this.getAddressInfo();
        synchronized (netStateLock) {
            loginReturned = false;//重置登录返回的标志位
        }
        byte[] data = InstructClientEncoder.login(getSequence(), address.getAccount(), address.getPassword());
        boolean sent = super.sendBytes(data);//发送登录指令
        if (!sent) {
            synchronized (netStateLock) {
                this.innerSession = null;
            }
            context.setNetState(false);
            invokeConnectError(callback, context);
        } else {
            try {
                long waitMillis = this.innerConnector.getConnectTimeoutMillis();
                synchronized (netStateLock) {
                    this.netStateLock.wait(waitMillis);//发送登录指令后开始等待
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean safeLoginReturned = false;
            synchronized (netStateLock) {
                safeLoginReturned = loginReturned;
            }
            if (!safeLoginReturned) {//没收到登录响应
                context.setNetState(false);
                synchronized (netStateLock) {
                    this.innerSession = null;
                }
                invokeConnectError(callback, context);
            } else {//收到了登录响应
                context.setNetState(true);
                boolean canDo = false;
                LoginResponse contextResult = null;
                synchronized (netStateLock) {
                    try {
                        contextResult = (LoginResponse) CommHelper.cloneObject(LoginResponseData);//复制一份
                        context.setResult(contextResult);
                        this.LoginResponseData = null;//清空本地
                        canDo = accessToken != null;
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                if (canDo) {//成功
                    super.invokeConnectSuccess(callback, context);
                } else {
                    synchronized (netStateLock) {
                        this.innerSession = null;
                    }
                    invokeConnectError(callback, context);
                }
            }
        }
    }

    @Override
    protected void initHandler(IoConnector connector) {
        connector.setHandler(new IoHandlerAdapter() {
            @Override
            public void messageReceived(IoSession ioSession, Object o) throws Exception {
                Log.e("InstructClientPipe", "###messageReceived###");
                boolean canDo = false;
                synchronized (netStateLock) {
                    if (InstructClientPipe.this.innerSession == ioSession) {
                        canDo = true;
                    }
                }
                if (canDo) {
                    IoBuffer ioBuffer = (IoBuffer) o;
                    byte[] msgBytes = new byte[ioBuffer.limit()];
                    ioBuffer.get(msgBytes);
                    byte[][] fullBags = msgLoader.fill(msgBytes);

                    if (fullBags != null && fullBags.length > 0) {
                        for (int i = 0; i < fullBags.length; i++) {
                            LoginResponse loginResponse = InstructClientDecoder.decodeToLoginResponse(fullBags[i]);
                            if (loginResponse != null) {
                                invokeLoginReturn(loginResponse);
                            } else {
                                if (accessToken != null) {
                                    //识别到KeepAlive消息时不再触发onMessage
                                    KeepAliveResponse klr = InstructClientDecoder.decodeToKeepAliveResponse(fullBags[i]);
                                    //klr == null表示非KeepAlive的响应
                                    if ((passKeepalive || (klr != null)) && InstructClientPipe.this.clientPipeListener != null) {
                                        InstructClientPipe.this.clientPipeListener.onMessage(InstructClientPipe.this, fullBags[i]);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        });
    }

    private void invokeLoginReturn(LoginResponse resultResponse) {
        synchronized (netStateLock) {
            if (!loginReturned) {
                loginReturned = true;
                this.LoginResponseData = resultResponse;

                if (this.LoginResponseData != null && this.LoginResponseData.result != null) {
                    accessToken = new String(this.LoginResponseData.result.accessToken);//记住关键的属性
                }
                this.netStateLock.notifyAll();
            }
            //开启心跳
            startKeepAlive();
        }
    }

    //开启心跳
    private void startKeepAlive() {
        if (keepAliveRunner == null) {
            keepAliveRunner = new Runner() {
                @Override
                protected long loopRun() {
                    return keepAliveWork();
                }
            };
        }
        keepAliveRunner.start(netAddressInfo.getName() + "-keepAlive");
    }

    @Override
    protected void invokeDisconnected(IoSession ioSession) {
        boolean canDo = false;
        synchronized (netStateLock) {
            if (innerSession == ioSession && accessToken != null) {
                canDo = true;
                innerSession = null;
                state = disconnectedState;
                if (canDo && this.keepAliveRunner != null) {
                    this.keepAliveRunner.stop();
                }
            }
        }
        if (canDo) {
            if (this.clientPipeListener != null) {
                this.clientPipeListener.onDisconnect(this);
            }
        }
    }

    @Override
    public boolean sendBytes(byte[] data) {
        synchronized (netStateLock) {
            if (this.accessToken != null) {
                return super.sendBytes(data);
            } else {
                return false;
            }
        }
    }

    @Override
    public int getState() {
        synchronized (netStateLock) {
            if (state == connectedState && !loginReturned) {
                return connectingState;
            }
            return super.getState();
        }
    }

    private long keepAliveWork() {
        synchronized (netStateLock) {
            if (accessToken != null) {
                byte[] data = InstructClientEncoder.keepAlive(getSequence(), accessToken);
                super.sendBytes(data);
            }
        }
        return keepAliveInterval;
    }
}
